<template>
    <div class="scanner-container">
        <video ref="videoRef" class="video-preview" playsinline></video>
        <div class="scan-overlay"></div>
        <div v-if="statusText" class="status">{{ statusText }}</div>
        <div class="footer-main">
            <button @click="handleCancelScan" class="cancel-btn">取消</button>
        </div>
    </div>
</template>

<script setup lang="ts">
import {ref, onMounted, onBeforeUnmount} from 'vue';
import {BrowserMultiFormatReader, type Result} from '@zxing/library';
import {showDialog} from "vant";

const emit = defineEmits<{
    (e: 'scan-success', isbn: string): void
    (e: 'scan-error', error: Error): void
    (e: 'scan-cancel'): void
}>();

const videoRef = ref<HTMLVideoElement | null>(null);
// 使用更长的超时时间初始化读取器
const codeReader = new BrowserMultiFormatReader(undefined, 5000);
const statusText = ref<string>('正在初始化摄像头...');
let scanTimer: number | null = null;

// ISBN校验函数
const validateISBN = (code: string): boolean => {
    if (code.length !== 13 && code.length !== 10) return false;
    // 这里可以添加更详细的校验逻辑
    return /^\d+$/.test(code);
};

// 添加节流函数
let isScanning = false;

// 检测是否为移动设备
const isMobileDevice = () => {
    return /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent);
};

// 开始扫描
const startScan = async () => {
    try {
        // 更新状态文本
        statusText.value = '正在获取摄像头...';
        console.log('开始获取摄像头');
        console.log('是否为移动设备:', isMobileDevice());
        
        // 设置获取摄像头的超时
        const timeoutPromise = new Promise<MediaStream>((_, reject) => {
            setTimeout(() => reject(new Error('获取摄像头超时')), 10000);
        });
        
        // 根据设备类型使用不同的约束条件
        let constraints: MediaStreamConstraints;
        
        if (isMobileDevice()) {
            // 移动设备使用更低的分辨率和强制后置摄像头
            constraints = {
                video: {
                    facingMode: { exact: 'environment' },  // 强制使用后置摄像头
                    width: { ideal: 480 },  // 降低分辨率
                    height: { ideal: 320 }  // 降低分辨率
                },
                audio: false
            };
        } else {
            // 桌面设备使用标准设置
            constraints = {
                video: {
                    width: { ideal: 640 },
                    height: { ideal: 360 }
                },
                audio: false
            };
        }
        
        let stream;
        try {
            console.log('尝试获取媒体流，约束条件:', JSON.stringify(constraints));
            // 使用Promise.race竞争，防止获取摄像头超时
            stream = await Promise.race([navigator.mediaDevices.getUserMedia(constraints), timeoutPromise]);
            console.log('成功获取媒体流');
        } catch (error) {
            console.error('获取媒体流失败，尝试使用更宽松的约束条件:', error);
            
            // 如果强制使用后置摄像头失败，尝试使用优先后置但不强制的设置
            try {
                console.log('尝试使用优先后置摄像头的设置...');
                stream = await navigator.mediaDevices.getUserMedia({
                    video: {
                        facingMode: 'environment',  // 优先使用后置摄像头，但不强制
                        width: { ideal: 480 },
                        height: { ideal: 320 }
                    },
                    audio: false
                });
                console.log('成功获取优先后置的摄像头');
            } catch (secondError) {
                console.error('无法获取优先后置摄像头，尝试使用任意可用摄像头:', secondError);
                
                // 如果仍然失败，尝试使用任意可用摄像头
                try {
                    console.log('尝试使用任意可用摄像头...');
                    stream = await navigator.mediaDevices.getUserMedia({ 
                        video: true,
                        audio: false
                    });
                    console.log('成功获取任意可用摄像头');
                } catch (fallbackError) {
                    if (fallbackError instanceof DOMException && fallbackError.name === 'NotAllowedError') {
                        console.error('用户拒绝了摄像头权限');
                        statusText.value = '请允许访问摄像头';
                        emit('scan-error', new Error('用户拒绝了摄像头权限'));
                        return;
                    }
                    console.error('无法获取任何摄像头，错误:', fallbackError);
                    statusText.value = '无法访问摄像头，请检查权限设置';
                    emit('scan-error', new Error('无法访问摄像头'));
                    return;
                }
            }
        }

        if (videoRef == null || !videoRef.value) {
            throw new Error('视频元素未初始化');
        }
        
        // 设置视频源并添加错误处理
        try {
            videoRef.value.srcObject = stream;
            console.log('已设置视频源');
        } catch (error) {
            console.error('设置视频源失败:', error);
            // 尝试使用旧方法
            try {
                // @ts-ignore - 兼容旧版浏览器
                videoRef.value.src = window.URL.createObjectURL(stream);
                console.log('使用 URL.createObjectURL 设置视频源');
            } catch (fallbackError) {
                console.error('所有设置视频源的方法都失败:', fallbackError);
                statusText.value = '无法显示摄像头画面';
                emit('scan-error', new Error('无法显示摄像头画面'));
                return;
            }
        }
        
        // 添加视频加载错误处理
        videoRef.value.onerror = (e) => {
            console.error('视频加载错误:', e);
            statusText.value = '视频加载失败';
            emit('scan-error', new Error('视频加载失败'));
        };
        
        videoRef.value.onloadedmetadata = () => {
            console.log('视频元数据已加载');
            statusText.value = '摄像头已就绪，识别中...';
            
            // 确保视频播放成功后再启动扫描
            videoRef.value.play().then(() => {
                console.log('视频播放已开始');
                animationFrameId = requestAnimationFrame(scanFrame);
            }).catch(err => {
                console.error('视频播放失败:', err);
                // 尝试自动播放策略
                videoRef.value.muted = true;
                videoRef.value.play().then(() => {
                    console.log('使用静音模式成功播放视频');
                    animationFrameId = requestAnimationFrame(scanFrame);
                }).catch(mutedErr => {
                    console.error('即使静音也无法播放视频:', mutedErr);
                    statusText.value = '无法播放视频，请检查浏览器设置';
                    emit('scan-error', new Error('无法播放视频'));
                });
            });
        };
    } catch (error) {
        console.error('摄像头初始化失败:', error);
        statusText.value = `初始化失败: ${error instanceof Error ? error.message : '未知错误'}`;
        emit('scan-error', error instanceof Error ? error : new Error('摄像头初始化失败'));
    }
};

let lastScanTime = 0;
let animationFrameId: number | null = null;
const SCAN_INTERVAL = 1000; // 增加到2秒扫描一次，减轻设备负担

const scanFrame = async (timestamp: number) => {
    // 如果上一次扫描还未完成，则跳过本次扫描
    if (isScanning) {
        console.log('上一次扫描尚未完成，跳过本次扫描');
        animationFrameId = requestAnimationFrame(scanFrame);
        return;
    }
    
    // 检查是否到达扫描间隔
    if (timestamp - lastScanTime >= SCAN_INTERVAL) {
        lastScanTime = timestamp;
        
        // 扫描逻辑...
        if (!videoRef.value || !videoRef.value.srcObject) {
            console.warn('视频元素或媒体流不存在，跳过扫描');
        } else {
            try {
                isScanning = true;
                
                // 使用Canvas捕获视频帧并只扫描中央区域
                const canvas = document.createElement('canvas');
                const context = canvas.getContext('2d');
                if (context && videoRef.value) {
                    const videoWidth = videoRef.value.videoWidth;
                    const videoHeight = videoRef.value.videoHeight;
                    
                    // 只截取中央区域进行扫描
                    const scanRegionSize = 0.7; // 扫描区域占视频的比例
                    const scanWidth = videoWidth * scanRegionSize;
                    const scanHeight = videoHeight * scanRegionSize;
                    const startX = (videoWidth - scanWidth) / 2;
                    const startY = (videoHeight - scanHeight) / 2;
                    
                    canvas.width = scanWidth;
                    canvas.height = scanHeight;
                    
                    // 只绘制中央区域
                    context.drawImage(
                        videoRef.value, 
                        startX, startY, scanWidth, scanHeight, // 源图像的裁剪区域
                        0, 0, scanWidth, scanHeight // 目标画布的绘制区域
                    );
                    
                    try {
                        // 从Canvas创建ImageBitmap
                        const imageData = context.getImageData(0, 0, canvas.width, canvas.height);
                        const bitmap = await createImageBitmap(imageData);
                        
                        // 使用decodeOnce方法从位图解码
                        // 将 ImageBitmap 转换为 HTMLImageElement
                        const img = document.createElement('img');
                        img.src = canvas.toDataURL();
                        const result = await codeReader.decodeOnce(img);
                        
                        if (result && validateISBN(result.getText())) {
                            console.log('成功扫描到ISBN:', result.getText());
                            emit('scan-success', result.getText());
                            stopScan();
                            return; // 扫描成功后停止动画帧
                        }
                    } catch (error) {
                        // 忽略解码错误
                        console.log('未识别到条码或ISBN无效:', error);
                    } finally {
                        // 清理资源
                        canvas.width = 0;
                        canvas.height = 0;
                    }
                }
            } catch (error) {
                console.log('扫描过程中出错:', error);
            } finally {
                isScanning = false;
            }
        }
    }
    
    // 仅当未停止时才继续请求下一帧
    if (animationFrameId !== null) {
        animationFrameId = requestAnimationFrame(scanFrame);
    }
};

const handleCancelScan = () => {
    stopScan()
    emit('scan-cancel');
};

const getBackCamera = async () => {
    const devices = await navigator.mediaDevices.enumerateDevices();
    const videoDevices = devices.filter(device => device.kind === 'videoinput');
    console.log('可用视频设备:', videoDevices.map(d => d.label));
    
    // 尝试找到后置摄像头
    const backCamera = videoDevices.find(device =>
        device.label.toLowerCase().includes('back') ||
        device.label.toLowerCase().includes('rear') ||
        device.label.toLowerCase().includes('环境') ||
        device.label.toLowerCase().includes('后置')
    );
    return backCamera ? backCamera.deviceId : null;
};

// 添加摄像头切换功能
const isFrontCamera = ref(false);

// 停止扫描
const stopScan = () => {
    if (animationFrameId !== null) {
        cancelAnimationFrame(animationFrameId);
        animationFrameId = null;
        console.log('动画帧已取消');
    }
    
    console.log('正在停止扫描...');
    
    // 清除扫描定时器
    if (scanTimer) {
        clearInterval(scanTimer);
        scanTimer = null;
        console.log('扫描定时器已清除');
    }
    
    // 停止所有媒体轨道
    if (videoRef.value?.srcObject) {
        // 释放 codeReader 资源
        try {
            codeReader.reset();
            console.log('条码读取器已重置');
        } catch (error) {
            console.error('重置条码读取器时出错:', error);
        }
        
        try {
            const stream = videoRef.value.srcObject as MediaStream;
            const tracks = stream.getTracks();
            console.log(`停止 ${tracks.length} 个媒体轨道`);
            tracks.forEach(track => {
                track.stop();
                console.log(`已停止轨道: ${track.kind}`);
            });
            // 清除视频源
            videoRef.value.srcObject = null;
            // 释放内存
            if (videoRef.value) {
                videoRef.value.onloadedmetadata = null;
                videoRef.value.onerror = null;
            }
            console.log('视频源已清除');
        } catch (error) {
            console.error('停止媒体轨道时出错:', error);
        }
    } else {
        console.log('没有活动的媒体流需要停止');
    }
    
    // 强制垃圾回收提示（不一定会立即执行）
    if (window.gc) {
        try {
            window.gc();
            console.log('已请求垃圾回收');
        } catch (e) {
            console.log('垃圾回收请求失败');
        }
    }
    
    statusText.value = '扫描已停止';
};

// 组件挂载时初始化摄像头
onMounted(() => {
    console.log('组件已挂载，准备初始化摄像头');
    try {
        // 使用setTimeout确保DOM完全渲染后再初始化摄像头
        setTimeout(() => {
            if (!videoRef.value) {
                console.error('视频元素未正确绑定');
                return;
            }
            startScan().catch(error => {
                console.error('摄像头初始化失败:', error);
                statusText.value = `初始化失败: ${error instanceof Error ? error.message : '未知错误'}`;
            });
        }, 300);
    } catch (error) {
        console.error('挂载阶段出错:', error);
    }
});

// 组件卸载前释放摄像头资源
onBeforeUnmount(() => {
    console.log('组件即将卸载，释放摄像头资源');
    try {
        stopScan();
    } catch (error) {
        console.error('卸载阶段释放资源出错:', error);
    }
});
</script>

<style scoped>
.scanner-container {
    max-width: 640px;
    margin: 0 auto;
    position: fixed;
    top: 15%;
    width: 90%;
    left: 50%;
    transform: translateX(-50%);
    box-shadow: 0 0 20px rgb(0 0 0 / 30%);
    border-radius: 12px;
    overflow: hidden;
    font-size: 0;
    height: 466px;
    display: flex;
    align-items: center;
    justify-content: center;
}

.video-preview {
    width: 100%;
    height: auto; /* 改为auto，让视频保持原始比例 */
    max-height: 466px; /* 限制最大高度 */
    background: #000;
    object-fit: contain; /* 保持原比例 */
    display: block; /* 确保视频元素正确显示 */
}

@media screen and (max-width: 768px) {
    /* 移动设备特殊处理 */
    .video-preview {
        width: 100%; /* 宽度占满 */
        height: auto; /* 高度自适应 */
        max-width: 100%;
    }
}
.scan-overlay {
    position: absolute;
    top: 50%;
    left: 50%;
    transform: translate(-50%, -50%);
    width: 70%;
    height: 20%;
    border: 3px solid #4CAF50;
    box-shadow: 0 0 20px rgba(76, 175, 80, 0.5);
}

.status {
    position: absolute;
    top: 50px;
    width: 100%;
    text-align: center;
    color: #fff;
    font-size: 16px;
    text-shadow: 0 0 5px rgba(0, 0, 0, 0.5);
}

.footer-main {
    position: absolute;
    bottom: 40px;
    text-align: center;
    width: 100%;
    height: 40px;
}
.cancel-btn{
    width: 100px;
    height: 40px;
    font-size: 14px;
    color: #ffffff;
    background-color: #00000000;
    border: 1px solid white;
    border-radius: 8px;
}
</style>
